{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 遗传算法的DNA片段"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import configure\n",
    "from music21 import *\n",
    "import random\n",
    "import ScoreAnalyzer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#根据数字生成音乐片段\n",
    "def generateScore(data):\n",
    "    melody = stream.Part()\n",
    "    melody.id = \"melody\"\n",
    "    harmony = stream.Part()\n",
    "    harmony.id = \"harmony\"\n",
    "\n",
    "    i = 0\n",
    "    while i < len(data):\n",
    "        #添加音符\n",
    "        midi = data[i]\n",
    "        quarter = (data[i + 1] % 16 + 1) / 4.0\n",
    "        if data[i + 2] > 115: # 休止符\n",
    "            thisRest = note.Rest(quarterLength=quarter)\n",
    "            melody.append(thisRest)\n",
    "        else:\n",
    "            thisNote = note.Note((midi % 12) + 60, quarterLength=quarter)\n",
    "            thisNote = note.Note(midi, quarterLength=quarter)\n",
    "            melody.append(thisNote)\n",
    "\n",
    "        #添加和弦\n",
    "        midi = [data[i + 3], data[i + 4], data[i + 5]]\n",
    "\n",
    "        quarter = (data[i + 1] % 16 + 1) / 4.0\n",
    "        if data[i + 7] > 115:\n",
    "            thisRest = note.Rest(quarterLength=quarter)\n",
    "            harmony.append(thisRest)\n",
    "        else:\n",
    "            thisChord = chord.Chord(midi, quarterLength=quarter)\n",
    "            harmony.append(thisChord)\n",
    "        i += 8\n",
    "\n",
    "    score = stream.Stream([melody, harmony])\n",
    "    return score"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#获取数据中的音律与和弦\n",
    "def generateDataScore(data):\n",
    "    melody = []\n",
    "    harmony = []\n",
    "\n",
    "    i = 0\n",
    "    while i < len(data):\n",
    "        midi = data[i]\n",
    "        quarter = (data[i + 1] % 16 + 1) / 4.0\n",
    "        # quarter = 2\n",
    "\n",
    "        if data[i + 2] > 115:\n",
    "            melody.append((-1, quarter))\n",
    "        else:\n",
    "            melody.append((midi, quarter))\n",
    "\n",
    "        midi = [data[i + 3], data[i + 4], data[i + 5]]\n",
    "        quarter = (data[i + 6] % 16 + 1) / 4.0\n",
    "        # quarter = 2\n",
    "\n",
    "        if data[i + 7] > 115:\n",
    "            harmony.append((-1, quarter))\n",
    "        else:\n",
    "            harmony.append((midi, quarter))\n",
    "        i += 8\n",
    "    dataScore = {\"melody\": melody, \"harmony\": harmony}\n",
    "    return dataScore"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#定义DNA类，每个DNA片段都存储一个隐喻信息\n",
    "class DNA:\n",
    "    def __init__(self, length):\n",
    "        data = []\n",
    "        # 每8位构成一个音符和一个和弦\n",
    "        for i in range(0, length * 8):\n",
    "            data.append(random.randint(0, 127))\n",
    "\n",
    "        self.data = data\n",
    "        self.dataScore = generateDataScore(self.data)\n",
    "        self.score = None\n",
    "\n",
    "        analyzer = ScoreAnalyzer.ScoreAnalyzer(self.dataScore)\n",
    "        self.fitness = analyzer.getAnalysisScore()\n",
    "    \n",
    "    #求取该DNA片段的适应度值\n",
    "    def getFitness(self, modifiers):\n",
    "        i = 0\n",
    "        sum = 0.0\n",
    "        modSum = 0.0\n",
    "        while i < len(self.fitness):\n",
    "            sum += self.fitness[i] * modifiers[i]\n",
    "            modSum += modifiers[i]\n",
    "            i += 1\n",
    "        return (sum / modSum)\n",
    "\n",
    "    def getFitnessArray(self):\n",
    "        return self.fitness\n",
    "\n",
    "    #交叉算子\n",
    "    def breed(self, partner):\n",
    "        if len(self.data) != len(partner.data):\n",
    "            raise ValueError(\"Attempted to breed DNA of differing lengths.\")\n",
    "\n",
    "        length = len(self.data)\n",
    "        midpoint = random.randint(0, length)\n",
    "\n",
    "        crossBred = []\n",
    "        for i in range(0, length):\n",
    "            if i < midpoint:\n",
    "                crossBred.append(self.data[i])\n",
    "            else:\n",
    "                crossBred.append(partner.data[i])\n",
    "\n",
    "        child = DNA(0)\n",
    "        child.data = crossBred\n",
    "        child.dataScore = generateDataScore(child.data)\n",
    "        child.score = None\n",
    "        analyzer = ScoreAnalyzer.ScoreAnalyzer(child.dataScore)\n",
    "        child.fitness = analyzer.getAnalysisScore()\n",
    "        return child\n",
    "\n",
    "    #变异算子\n",
    "    def mutate(self, rate):\n",
    "        changed = False\n",
    "        if random.random() > (rate * 5):\n",
    "            return\n",
    "\n",
    "        for i in range(0, len(self.data)):\n",
    "            if random.random() < rate:\n",
    "                changed = True\n",
    "                self.data[i] = random.randint(0, 127)\n",
    "        if changed:\n",
    "            self.dataScore = generateDataScore(self.data)\n",
    "\n",
    "    #返回该DNA存储的音乐数字\n",
    "    def getDate(self):\n",
    "        return self.data\n",
    "\n",
    "    #根据该DNA的数据生成一个音乐片段\n",
    "    def getScore(self):\n",
    "        if self.score is None:\n",
    "            self.score = generateScore(self.data)\n",
    "        return self.score"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:Empty]",
   "language": "python",
   "name": "conda-env-Empty-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
